import $ from 'jquery';
import fieldRegistry, {layoutRegistry} from "@/components/form/FieldRegistry";
import {openModal} from "@/utils";
import LayoutConfig from "@/components/form/layout/LayoutConfig.vue";

class FormDefinitionEditor {
    ctx;
    dragComponentShadow;
    draggingStartPoint;

    constructor(ctx) {
        this.ctx = ctx;
    }

    bind() {
        this.init()
        this.bindDrag()
        this.bindFieldName()
        this.bindSelectField();
        this.bindLayoutIcon();
        this.bindLayoutAction();
    }

    init() {
        $('[data-role="icon"]').attr('draggable', 'true');
    }

    bindDrag() {
        this.bindDragIcon()
        this.bindDragBlock()
    }

    bindDragIcon() {
        let that = this
        let placeholder = undefined;
        let comp = undefined

        function dragover(e) {
            let target = e.target;
            target = that.getElementUntilHasRole(target);
            let role = that.getElementRole(target);

            if (!role) {
                $(placeholder).remove();
                return;
            }

            if (role === 'placeholder') return that.stop(e);
            if (role === 'root') {
                that.stop(e);
                $(target).append(placeholder);
            }
        }

        function dragend() {
            if (placeholder[0].parentNode) {
                let field = that.createFieldByComponentName(comp)
                let fieldElement = that.createFieldElement(field)
                $(placeholder).replaceWith(fieldElement);
            }
            $(document).unbind('dragover');
            $('[data-role="icon"]').unbind('dragend');
            that.bind();
        }

        function dragstart({target}) {
            placeholder = that.createPlaceholder()
            comp = $(target).attr('data-component')
            $(document).unbind('dragover').on('dragover', dragover)
            $('[data-role="icon"]').unbind('dragend').on('dragend', dragend)
        }

        $('[data-role="icon"]').unbind('dragstart').on('dragstart', dragstart)
    }

    bindDragBlock() {
        $('[data-role="block"]').attr('draggable', true);

        let that = this, draggingElement = undefined
        let placeholder = that.createPlaceholder();
        let flag = false

        function dragover(e) {
            let {target, clientX, clientY} = e;
            that.moveDragComponentShadow(e);
            target = that.getElementUntilHasRole(target);
            if (!target) return;
            let rect = target.getBoundingClientRect();
            let role = that.getElementRole(target);

            if (role === 'root' || role === 'block-container') {
                $(target).append(placeholder);
                flag = true;
            }

            if (role === 'block') {
                if (draggingElement === target) return;
                if (rect.top + 5 >= clientY) {
                    if ($(target).prev()[0] === draggingElement) return;
                    //插入到这个元素的前面
                    $(target).before(placeholder)
                    flag = true
                } else {
                    if ($(target).next()[0] === draggingElement) return;
                    //插入到这个元素的后面
                    $(target).after(placeholder)
                    flag = true
                }
            }
        }

        function dragend() {
            if (flag && placeholder[0].parentNode) {
                $(placeholder).replaceWith(draggingElement);
            }
            that.stopDragComponentShadow();
            that.draggedField(draggingElement);
            $(document).unbind('dragover');
            $(document).unbind('dragend');
        }


        function dragstart(e) {
            let {target, clientX, clientY} = e;
            draggingElement = target = that.getElementUntilHasRole(target);
            if (!target) return
            let role = that.getElementRole(target);
            that.startDragComponentShadow(target, clientX, clientY, e)
            that.draggingField(target)

            $(document).unbind('dragover').on('dragover', dragover)
            $(document).unbind('dragend').on('dragend', dragend)
        }

        $('[data-role="block"]').unbind('dragstart').on('dragstart', dragstart)
    }

    moveDragComponentShadow({clientX, clientY}) {
        $(this.dragComponentShadow).css('left', `${this.draggingStartPoint.left + clientX - this.draggingStartPoint.startX}px`);
        $(this.dragComponentShadow).css('top', `${this.draggingStartPoint.top + clientY - this.draggingStartPoint.startY}px`);
    }

    stopDragComponentShadow() {
        $(this.dragComponentShadow).remove();
    }

    startDragComponentShadow(ele, startX, startY, e) {
        e = e.originalEvent || e;
        e.dataTransfer.setDragImage(document.createElement('div'), 0, 0);

        this.dragComponentShadow = $(ele).clone();
        $(document.body).append(this.dragComponentShadow);
        $(this.dragComponentShadow).addClass('dragging-block-shadow')
        let rect = ele.getBoundingClientRect();

        $(this.dragComponentShadow).css('width', `${rect.width}px`)
        $(this.dragComponentShadow).css('height', `${rect.height}px`)
        $(this.dragComponentShadow).css('left', `${rect.left}px`)
        $(this.dragComponentShadow).css('top', `${rect.top}px`);

        this.draggingStartPoint = {startX, startY, left: rect.left, top: rect.top};
    }

    draggingField(target) {
        $(target).addClass('dragging-block')
    }

    draggedField(target) {
        $(target).removeClass('dragging-block')
    }

    createFieldByComponentName(comp) {
        let res = fieldRegistry[comp].config.creatField();
        this.ctx.onCreateField && this.ctx.onCreateField(res);
        return res;
    }

    createFieldElement(field) {
        return this.doCreateFieldElement(field.id)
    }

    doCreateFieldElement(id) {
        return $(`<div data-role="block" data-id="${id}" data-view-type="field" class="field-component"></div>`)[0]
    }

    getElementUntilHasRole(target) {
        while (target && !$(target).attr('data-role')) target = target.parentNode;
        return target;
    }

    createPlaceholder() {
        return $('<div class="drag-placeholder" data-role="placeholder">拖拽至此</div>')
    }

    stop(e) {
        try {
            e.stopPropagation();
            e.preventDefault();
        } catch (e) {
        }
    }

    bindFieldName() {
        let formDefinition = this.ctx.getFormDefinition();
        (formDefinition.fields || []).forEach(field => {
            $(`[data-id=${field.id}]`).html(`<span>${field.label}</span>`);
        })
    }

    bindSelectField() {
        let that = this;
        $('[data-role="root"]').unbind('click').on('click', ({target}) => {
            target = that.getElementUntilHasRole(target);
            if (!target) return;
            let role = that.getElementRole(target);
            if(role !== 'block') return;

            that.editinged()
            that.editingField(target)
            let id = that.getElementDataId(target)
            let formDefinition = that.ctx.getFormDefinition();
            let field = (formDefinition.fields || []).find(f => f.id === id);
            field && this.ctx.onSelectField(field);
        })
    }

    bindLayoutIcon() {
        let that = this, placeholder = that.createPlaceholder();
        $('[data-role=icon-layout]').attr('draggable', 'true');
        let draggingEle = null, draggingComponent = undefined;


        function dragend() {
            if (placeholder[0].parentNode) {
                that.createLayoutElement(draggingComponent, element => {
                    $(placeholder).replaceWith(element);
                    that.bind();
                })
            }
            $(document).unbind('dragover');
            $(draggingEle).unbind('dragend')
            that.bind();
        }

        function dragover(e) {
            let {target} = e;
            target = that.getElementUntilHasRole(target);
            if (!target) return $(placeholder).remove();

            let role = that.getElementRole(target);
            if (role === 'root') {
                that.getRootContainer().append(placeholder);
                that.stop(e);
                return;
            }

            $(document).unbind('dragover').on('dragover', dragover)
            $(draggingEle).unbind('dragend').on('dragend', dragend)
        }

        function dragstart({target}) {
            draggingEle = target;
            target = that.getElementUntilHasRole(target);
            draggingComponent = $(target).attr('data-component')
            $(document).unbind('dragover').on('dragover', dragover)
        }

        $('[data-role=icon-layout]').unbind('dragstart').on('dragstart', dragstart)
    }


    createLayoutElement(draggingComponent, callback) {
        let layoutData = {}, that = this;
        let vnode = this.ctx.$createElement(LayoutConfig, {
            props: {layoutData},
            on: {
                'update:row': r => layoutData.row = r,
                'update:col': r => layoutData.col = r,
            }
        });
        openModal(vnode, {
            title: '请输入行和列',
            close: () => {
                that.doCreateLayoutElement(draggingComponent, layoutData, callback)
            }
        });
    }

    bindLayoutAction() {
        for (let key in layoutRegistry) {
            layoutRegistry[key] &&
            layoutRegistry[key].comp &&
            layoutRegistry[key].comp.bind &&
            layoutRegistry[key].comp.bind();
        }
    }

    doCreateLayoutElement(draggingComponent, layoutData, callback) {
        callback && callback(layoutRegistry[draggingComponent].config.createElement(layoutData));
    }

    getRootContainer() {
        return $('[data-role="root"]')
    }

    editinged() {
        $('.editing-field').removeClass('editing-field')
        this.ctx.onClearSelectField();
    }

    editingField(ele) {
        $(ele).addClass('editing-field')
    }

    getElementDataId(target) {
        return $(target).attr('data-id')
    }

    getElementRole(target) {
        return $(target).attr('data-role');
    }
}

export default FormDefinitionEditor;